/*
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

package com.facebook.network.connectionclass;


import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;


import java.util.concurrent.atomic.AtomicInteger;


/**
 * Class used to read from TrafficStats periodically, in order to determine a ConnectionClass.
 */
public class DeviceBandwidthSampler {

    /**
     * The DownloadBandwidthManager that keeps track of the moving average and ConnectionClass.
     */
    private final ConnectionClassManager mConnectionClassManager;

    private AtomicInteger mSamplingCounter;

    private SamplingHandler mHandler;

    private long mLastTimeReading;
    private long sPreviousBytes = -1;

    // Singleton.
    private static class DeviceBandwidthSamplerHolder {
        public static final DeviceBandwidthSampler instance =
                new DeviceBandwidthSampler(ConnectionClassManager.getInstance());
    }

    /**
     * Retrieval method for the DeviceBandwidthSampler singleton.
     *
     * @return The singleton instance of DeviceBandwidthSampler.
     */

    public static DeviceBandwidthSampler getInstance() {
        return DeviceBandwidthSamplerHolder.instance;
    }

    private DeviceBandwidthSampler(
            ConnectionClassManager connectionClassManager) {
        mConnectionClassManager = connectionClassManager;
        mSamplingCounter = new AtomicInteger();

        mHandler = new SamplingHandler(EventRunner.create("ParseThread"));
    }

    /**
     * Method call to start sampling for download bandwidth.
     */
    public void startSampling() {
        if (mSamplingCounter.getAndIncrement() == 0) {
            mHandler.startSamplingThread();
            mLastTimeReading = ohos.miscservices.timeutility.Time.getRealTime();
        }
    }

    /**
     * Finish sampling and prevent further changes to the
     * ConnectionClass until another timer is started.
     */
    public void stopSampling() {
        if (mSamplingCounter.decrementAndGet() == 0) {
            mHandler.stopSamplingThread();
            addFinalSample();
        }
    }

    /**
     * Method for polling for the change in total bytes since last update and
     * adding it to the BandwidthManager.
     */
    protected void addSample() {
        long newBytes = ohos.net.DataFlowStatistics.getAllRxBytes();
        long byteDiff = newBytes - sPreviousBytes;
        if (sPreviousBytes >= 0) {
            synchronized (this) {
                long curTimeReading = ohos.miscservices.timeutility.Time.getRealTime();
                mConnectionClassManager.addBandwidth(byteDiff, curTimeReading - mLastTimeReading);

                mLastTimeReading = curTimeReading;
            }
        }
        sPreviousBytes = newBytes;
    }

    /**
     * Resets previously read byte count after recording a sample, so that
     * we don't count bytes downloaded in between sampling sessions.
     */
    protected void addFinalSample() {
        addSample();
        sPreviousBytes = -1;
    }

    /**
     * make sure there are still threads which are sampling
     *
     * @return True if there are still threads which are sampling, false otherwise.
     */
    public boolean isSampling() {
        return (mSamplingCounter.get() != 0);
    }

    private class SamplingHandler extends EventHandler {
        /**
         * Time between polls in ms.
         */
        static final long SAMPLE_TIME = 1000;

        static private final int MSG_START = 1;


        public SamplingHandler(EventRunner runner) throws IllegalArgumentException {
            super(runner);
        }

        @Override
        protected void processEvent(InnerEvent event) {
            super.processEvent(event);
            switch (event.eventId) {
                case MSG_START:
                    addSample();
                    sendEvent(MSG_START, SAMPLE_TIME);
                    break;
                default:
                    throw new IllegalArgumentException("Unknown eventId=" + event.eventId);
            }
        }

        public void startSamplingThread() {
            sendEvent(SamplingHandler.MSG_START);

        }

        public void stopSamplingThread() {
            removeEvent(SamplingHandler.MSG_START);
        }
    }

}